#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

#include "../ip.h"

uint32_t subnet_id(uint32_t addr, uint32_t mask)
{
    uint32_t id = addr & mask;
    if(0 == id)
    {
        fprintf(stderr, "");
        return 0;
    }
}

uint32_t subnet_broadcast(uint32_t addr, uint32_t mask)
{
    return subnet_id(addr, mask) | (~mask);
}

int subnet_range(uint32_t addr, uint32_t mask, uint32_t* start, uint32_t* end)
{
    uint32_t id = subnet_id(addr, mask);
    if(0 == id)
    {
        return 0;
    }

    uint32_t broadcast = subnet_broadcast(addr, mask);
    if(ntohl(mask) <= 0xfffffffc)
    {
        *start = htonl(ntohl(id) + 1);
        *end = htonl(ntohl(broadcast) - 1);
    }
    else
    {
        *start = *end = id;
    }
    

    return 1;
}

inline uint16_t ipv4hdr_chksum(uint16_t *w, int blen)
{
    uint32_t cksum;

    /* IP must be >= 20 bytes */
    cksum  = w[0];
    cksum += w[1];
    cksum += w[2];
    cksum += w[3];
    cksum += w[4];
    cksum += w[5];
    cksum += w[6];
    cksum += w[7];
    cksum += w[8];
    cksum += w[9];

    blen  -= 20;
    w     += 10;

    while(blen)    /* IP-hdr must be an integral number of 4 byte words */
    {
        cksum += w[0];
        cksum += w[1];
        w     += 2;
        blen  -= 4;
    }

    cksum  = (cksum >> 16) + (cksum & 0x0000ffff);
    cksum += (cksum >> 16);

    return (uint16_t)(~cksum);
}


inline int subnet_info_init(struct subnet_info* p, uint32_t addr, int mask_bits)
{
    p->netmask_h = 0xffffffff;
    if (mask_bits == 0)
    {
        p->netmask_h = 0;
    }
    else
    {
        p->netmask_h = (p->netmask_h >> (32 - mask_bits) ) << (32 - mask_bits);
        p->netmask = htonl(p->netmask_h);
    }
    
    p->id = subnet_id(addr, p->netmask);
    if(0 == p->id)
    {
        fprintf(stderr, "%s - Ip address 0.0.0.0 is a reserved source address meaning \"this host on the network \" and cannot be subnetted.\r\n");
        return false;
    }
    else
    {
        subnet_range(addr, p->netmask, &p->start, &p->end);
        p->start_h = ntohl(p->start);
        p->end_h = ntohl(p->end);
        p->host_count = p->end_h - p->start_h + 1;

        return true;
    }
}



